/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <arch/pagetable.h>
#include <core/mm.h>
#include <core/spinlock.h>
#include <caps/task.h>
#include <caps/cnode.h>

void tcb_recycle(tcb_t *tcb)
{
    if (cap_is_empty(&tcb->cnode) == 0) {
        cap_cnode_put(&tcb->cnode);
    }

    if (tcb->addrspace.pg != NULL) {
        addrspace_put(&tcb->addrspace);
    }

    if (tcb->ipcbuffer != NULL) {
        /* PRQA S 0326 1 */
        (void)allocator_free((unsigned long)tcb->ipcbuffer, sizeof(ipcbuffer_t));
    }

    (void)allocator_free((unsigned long)tcb, sizeof(tcb_t));

    endpoint_clean_callers(tcb);
}

void task_init(tcb_t *tcb, uint32_t type)
{
    int                 i;
    static unsigned int thread_id = 10;
    context_init(task_get_context(tcb), (int)type);

    tcb->type  = type;
    tcb->magic = TCB_MAGIC;
    tcb->id    = thread_id;
    thread_id++;
    tcb->affinity = 0;
    tcb->cpu_set  = AFFINITY_CPU0;

    tcb->timeslice.readytime.time = timer_current().time;
    tcb->timeslice.timeslice      = CONFIG_DEFUALT_TIMESLICE;
    tcb->timeslice.timeleft       = tcb->timeslice.timeslice;

    tcb->currentts = &(tcb->timeslice);

    tcb->sched_policy = SCHED_FIFO;

    tcb->state  = TASK_INACTIVE;
    tcb->flags  = 0;
    tcb->domain = 0;

    tcb->maxpriority  = CONFIG_DEFAULT_PRIORITY;
    tcb->priority     = CONFIG_DEFAULT_PRIORITY;
    tcb->orig_priority = CONFIG_DEFAULT_PRIORITY;
    tcb->ipcbuffer    = NULL;
    tcb->faulthandler = NULL;

    tcb->ss_low_priority = CONFIG_MAX_PRIORITY -1;
    tcb->ss_init_budget = 50000;
    tcb->ss_repl_period = 100000;
    tcb->ss_max_repl = 1;
    tcb->repl_count = 0;
    tcb->curr_budget = 0;
    tcb->activation_time = 0;

    tcb->proc_idx = 0;
    tcb->owner    = NULL;
    tcb->rtn      = NULL;
    tcb->detached = '\0';

    tcb->cancelstate = '\0';
    tcb->canceltype  = '\0';
    tcb->canceled    = '\0';

    tcb->woken_by_cancel = '\0';
    tcb->condvar_avail   = '\0';

    for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
        tcb->specific[i] = NULL;
    }

    tcb->cleanup = NULL;

    tcb->calledep      = NULL;
    tcb->mm.mmap_index = 0;

    list_init(&tcb->callernode);
    list_init(&tcb->callers);
    list_init(&tcb->replenish.t_sched_list);

    task_set_name(tcb, "Unset");
    cap_make_empty(&tcb->cnode);
    list_init(&tcb->runlist);
    object_init(&tcb->obj);
    object_init(&tcb->addrspace.obj);
}

void task_switch_prepare(tcb_t *to, tcb_t *from)
{
    (void)from;

#ifdef DISABLE_MMU_ASID
    page_table_switch_to(to->addrspace.pg, (to->type == TTYPE_USER) ? 0 : 1);
#else
    page_table_switch_to_asid(to->addrspace.pg, to->addrspace.asid, (to->type == TTYPE_USER) ? 0 : 1);

#endif
}

void task_clone(tcb_t *target, tcb_t *src, unsigned long id)
{
    (void)id;
    tcb_get(src);

    cap_cnode_copy(&target->cnode, &src->cnode);

    prepare_thread_addrspace(&target->addrspace, &src->addrspace);

    if (src->faulthandler != NULL) {
        endpoint_get(src->faulthandler);
        target->faulthandler = src->faulthandler;
    }

    target->priority    = src->priority;
    target->maxpriority = src->maxpriority;
#ifdef CONFIG_NEW_TIME
    target->timeslice.readytime = src->timeslice.readytime;
    target->timeslice.timeslice = src->timeslice.timeslice;
    target->timeslice.timeleft  = target->timeslice.timeslice;
#else
    target->readytime = src->readytime;
    target->timeslice = src->timeslice;
#endif

    (void)tcb_put(src);
}
